import keras
keras.__version__
import matplotlib.pyplot as plt
from keras import regularizers
from keras.datasets import imdb
import numpy as np

# 过拟合和欠拟合
# 在前面章节的实验中，我们会看到，我们模型的性能，对于验证集而言，达到顶峰后就会开始下降。
# 机器学习最基本的问题就是在拟合（optimization）和泛化能力之间取一个折衷。
# 在开始训练时，优化和泛化是相关的，训练集的loss越低，那么测试集的loss就越低。
# 当这个发生时，你的模型是欠拟合的，你的模型仍然有进步的空间。
# 模型还没有对所有的训练数据建模
# 但是在对训练数据进行一段时间的迭代以后，泛化能力开始下降，验证集的参数开始下降。
# 模型开始过拟合，模型开始学习对于训练集而言的特定的pattern，但是对于新数据而言是误导甚至是错误的。

# 为了阻止模型学习训练集中误导或者无关的pattern（输入），最好的方式是获得更多的训练集。
# 一个在更多模型上训练的模型自然会有更好的泛化能力。
# 如果没有更多的提升可能，那么下一个最好的方式就是调整你模型允许存储的信息，或者对它允许存储的信息增加约束。

# 如果神经网络只能承受记住一部分输入信息，那么这个优化过程会驱使他记住最频繁出现的（prominent）输入数据，这样他才会有更大的概率来繁华良好。

# 处理过拟合的方式之一是正则化。
# 下面把这个技术用到我们的影评分类实验中。

(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)

def vectorize_sequences(sequences, dimension=10000):
    # Create an all-zero matrix of shape (len(sequences), dimension)
    results = np.zeros((len(sequences), dimension))
    for i, sequence in enumerate(sequences):
        results[i, sequence] = 1.  # set specific indices of results[i] to 1s
    return results

#---------------------下面是训练集和验证集---------------------------------
# Our vectorized training data
x_train = vectorize_sequences(train_data)
# Our vectorized test data
x_test = vectorize_sequences(test_data)
# Our vectorized labels
y_train = np.asarray(train_labels).astype('float32')
y_test = np.asarray(test_labels).astype('float32')


# 克服过拟合
# 
# 减少神经网络的大小
# 最简单的克服过拟合的方式就是裁剪模型的规模，也就是模型中可学习的参数（这些参数是由层数以及每一层中的顶点数来确定的）

# 在深度学习中，模型的可学习参数经常被认为是模型的“capacity”。
# 本质上，如果一个模型有更多的参数，那么也就有更多的 "memorization capacity" 
# 这样有助于学习一个像字典一样的训练集和标签y之间的映射，一宗没有泛化能力的映射。

# 例如，
# For instance, 如果一个模型有500,000的参数来学习每个数字的类别（MNIST数据集），也就是我们对于每个数字使用10个参数，
# 那么这样的模型对于新的数据集而言，是无用的（言外之意，太死板了）

# 请记住，深度学习的挑战在于泛化能力，而不是拟合。

# 另一方面，如果网络只有有限的资源，他可能无法轻易地学习这种映射，
# 为了最小化这种损失，我们需要求助于“学习压缩表达”，这种表达具备预测性能力，尤其是我们感兴趣的表达类型。
# 同时，请记住，你应该使用具备足够参数的模型，这样不会导致欠拟合。
# 你的模型不应该缺少记忆资源
# 在"too much capacity" 和 "not enough capacity"之间需要有一种折中。


# 不幸的是，没有魔法公式来告诉你正确的层数是什么，或者每层的隐藏节点应该是多少个。
# 你需要评估一系列不同的神经网络结构（基于你的验证集，当然不是你的测试集），以便于对你的数据集获取合理的模型规模。
# 主要的工作流程是，发现一个合理的模型规模，以较少的层数和参数开始，
# 提高每层的隐藏单元数量以及增加层数，直到你看到验证集的loss减少。
#让我们继续影评数据分类任务。


from keras import models
from keras import layers

original_model = models.Sequential()
original_model.add(layers.Dense(16, activation='relu', input_shape=(10000,)))
#里面的16就是隐藏节点数量
# 
original_model.add(layers.Dense(16, activation='relu'))
original_model.add(layers.Dense(1, activation='sigmoid'))

original_model.compile(optimizer='rmsprop',
                       loss='binary_crossentropy',
                       metrics=['acc'])


# Now let's try to replace it with this smaller network:




smaller_model = models.Sequential()
smaller_model.add(layers.Dense(4, activation='relu', input_shape=(10000,)))
# 在input shape中有三个参数,smaples/timesteps/dim
smaller_model.add(layers.Dense(4, activation='relu'))
smaller_model.add(layers.Dense(1, activation='sigmoid'))

smaller_model.compile(optimizer='rmsprop',
                      loss='binary_crossentropy',
                      metrics=['acc'])


# 这里是为了在“最初的神经网络”和“更小的神经网络”之间比较验证集的loss

# The dots are the validation loss values of 
# the smaller network, and the crosses are the initial network 
# (remember: a lower validation loss signals a better model).




original_hist = original_model.fit(x_train, y_train,
                                   epochs=20,#①
                                   batch_size=512,
                                   validation_data=(x_test, y_test))





smaller_model_hist = smaller_model.fit(x_train, y_train,
                                       epochs=20,#②
                                       batch_size=512,
                                       validation_data=(x_test, y_test))





epochs = range(1, 21)#③
original_val_loss = original_hist.history['val_loss']
smaller_model_val_loss = smaller_model_hist.history['val_loss']







# b+ is for "blue cross"
plt.plot(epochs, original_val_loss, 'b+', label='Original model')
# "bo" is for "blue dot"
plt.plot(epochs, smaller_model_val_loss, 'bo', label='Smaller model')
plt.xlabel('Epochs')
plt.ylabel('Validation loss')
plt.legend()
plt.show()


# 你可以看到，“较小的神经网络”相比与"参考神经网络"更迟开始过拟合
# 并且在过拟合以后，他的性能退化更慢